import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:shirne_dialog/shirne_dialog.dart';

import '../utils/api.dart';
import '../application.dart';
import '../utils/logger.dart';
import 'tags/divider.dart';
import 'tags/empty.dart';

abstract class ListStatePage<T extends StatefulWidget, M> extends State<T> {
  List<M> lists = [];

  bool hasMore = true;

  /// 首帧在initState中直接调用，否则addPostFrameCallback会触发一个Bug
  /// https://github.com/flutter/flutter/issues/74651
  bool isLoading = true;
  bool isError = false;

  int page = 1;
  int totalCount = 0;
  int totalPage = 1;

  String get apiUrl;
  Map<String, dynamic> get requestArgs => {};

  M transItem(dynamic item);

  Widget buildItem(M item);

  @override
  initState() {
    super.initState();
    loadData();
  }

  buildGridView({
    int crossAxisCount = 2,
    double aspectRatio = 1,
    double mainAxisSpacing = 10,
    double crossAxisSpacing = 10,
  }) {
    return RefreshIndicator(
      onRefresh: reloadData,
      child: GridView.builder(
        physics: const AlwaysScrollableScrollPhysics()
          ..applyTo(const BouncingScrollPhysics()),
        itemCount: lists.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: crossAxisCount,
          childAspectRatio: aspectRatio,
          mainAxisSpacing: mainAxisSpacing,
          crossAxisSpacing: crossAxisSpacing,
        ),
        itemBuilder: (BuildContext context, int index) {
          return buildItem(lists[index]);
        },
      ),
    );
  }

  buildListView() {
    return RefreshIndicator(
      onRefresh: reloadData,
      child: ListView.builder(
        physics: const AlwaysScrollableScrollPhysics()
          ..applyTo(const BouncingScrollPhysics()),
        itemCount: lists.length + 1,
        itemBuilder: (BuildContext context, int index) {
          if (index >= lists.length) {
            if (isLoading) {
              return renderLoading();
            }
            if (isError) {
              return renderError();
            }
            if (hasMore) {
              SchedulerBinding.instance.addPostFrameCallback(loadData);
              return renderLoading();
            }
            if (lists.isEmpty) {
              return renderEmpty();
            }
            return renderNoMore();
          }
          return buildItem(lists[index]);
        },
      ),
    );
  }

  /// 渲染没有更多组件
  Widget renderNoMore() {
    return const TagDivider(
      '没有更多了',
      dividerWidth: 50,
    );
  }

  /// 渲染空列表组件
  Widget renderEmpty() {
    return const TagEmpty('没有相关内容');
  }

  /// 渲染加载出错组件，必须在组件中绑定tap事件调用loadData
  Widget renderError() {
    return TagDivider(
      '加载错误，点击重试',
      onTap: loadData,
    );
  }

  /// 渲染正在加载组件，可根据lists.isEmpty 区分首次加载
  Widget renderLoading() {
    return Center(
      child: Container(
        width: 30,
        height: 30,
        margin: const EdgeInsets.symmetric(vertical: 20),
        child: const CircularProgressIndicator(
          color: Colors.black26,
          strokeWidth: 2,
        ),
      ),
    );
  }

  /// 重新加载列表
  Future<void> reloadData() async {
    setState(() {
      page = 1;
      hasMore = true;
      lists = [];
    });
    return await loadData();
  }

  /// 增量加载数据
  Future<void> loadData([_]) async {
    logger.info('loadData');
    setState(() {
      isError = false;
      isLoading = true;
    });
    final args = requestArgs;
    args['page'] = page;
    final result = await Api.post(apiUrl, args);
    if (result.code == 1) {
      Map<String, dynamic> data = result.data!;
      setState(() {
        lists.addAll((data['lists'] as List<dynamic>).map<M>(transItem));
        page = data['page'] ?? 1;
        totalCount = data['count'] ?? 0;
        totalPage = data['total_page'] ?? 0;
        if (page >= totalPage) {
          hasMore = false;
        } else {
          page++;
        }
        isLoading = false;
      });
    } else {
      MyDialog.toast(result.message);
      if (result.code == 99) {
        app.route('/login');
      }
      setState(() {
        isLoading = false;
        isError = true;
      });
    }
  }
}
